home *** CD-ROM | disk | FTP | other *** search
- /* ================================================================== *
- * Editor mined *
- * Part 2 *
- * ================================================================== */
-
- #include "mined.h"
-
- /* ================================================================== *
- * Definitions specific for mined2.c *
- * ================================================================== */
-
- #ifndef copycommand
- # ifdef vms
- # define copycommand "copy %s %s"
- # endif
- # ifdef msdos
- # define copycommand "copy %s %s > nul:"
- # endif
- #endif
-
- /*
- * viewonlyerr () outputs an error message with a beep
- */
- void
- viewonlyerr ()
- {
- ring_bell ();
- error ("View only mode", NIL_PTR);
- }
-
- FLAG yank_status = NOT_VALID; /* Status of yank_file */
-
- /* ================================================================== *
- * Forward declarations *
- * ================================================================== */
-
- FLAG delete_text ();
- void file_insert ();
- void search ();
- void re_search ();
- void prev_search ();
- void search_for ();
- void yank ();
- FLAG compile ();
- int reverse_scroll ();
- int find_y ();
- int forward_scroll ();
- int insert ();
- int legal ();
- int line_check ();
- int check_string ();
- int in_list ();
- int star ();
- FLAG checkmark ();
-
- /* ================================================================== *
- * Move Commands *
- * ================================================================== */
-
- /*
- * Move one line up.
- */
- void
- MUP ()
- {
- if (hop_flag > 0) HIGH ();
- else if (y == 0) { /* Top line of screen. Scroll one line */
- if (reverse_scroll (TRUE) != ERRORS) {
- move_y (y);
- }
- }
- else /* Move to previous line */
- move_y (y - 1);
- }
-
- /*
- * Move one line down.
- */
- void
- MDN ()
- {
- if (hop_flag > 0) LOW ();
- else if (y == last_y) { /* Last line of screen. Scroll one line */
- if (bot_line->next == tail && bot_line->text [0] != '\n') {
- /* dummy_line (); don't create new empty line ! */
- /* MDN (); */
- return;
- }
- else {
- (void) forward_scroll (TRUE);
- move_y (y);
- }
- }
- else /* Move to next line */
- move_y (y + 1);
- }
-
- /*
- * Move left one position.
- */
- void
- MLF ()
- {
- if (hop_flag > 0) BLINE ();
- else if (x == 0 && get_shift (cur_line->shift_count) == 0) {/* Begin of line */
- if (cur_line->prev != header) {
- MUP (); /* Move one line up */
- move_to (LINE_END, y);
- }
- }
- else
- move_to (x - 1, y);
- }
-
- /*
- * Move right one position.
- */
- void
- MRT ()
- {
- if (hop_flag > 0) ELINE ();
- else if (* cur_text == '\n') {
- if (cur_line->next != tail) { /* Last char of file */
- MDN (); /* Move one line down */
- move_to (LINE_START, y);
- }
- }
- else
- move_to (x + 1, y);
- }
-
- /*
- * Move to top of screen
- */
- void
- HIGH ()
- {
- move_y (0);
- }
-
- /*
- * Move to bottom of screen
- */
- void
- LOW ()
- {
- move_y (last_y);
- }
-
- /*
- * Move to begin of line.
- */
- void
- BLINE ()
- {
- move_to (LINE_START, y);
- }
-
- /*
- * Move to end of line.
- */
- void
- ELINE ()
- {
- move_to (LINE_END, y);
- }
-
- /*
- * GOTO () prompts for a linenumber and moves to that line.
- */
- void
- goline (number)
- int number;
- {
- LINE * line;
- if (number <= 0 || (line = proceed (header->next, number - 1)) == tail)
- error ("Illegal line number: ", num_out ((long) number));
- else {
- clear_status ();
- move_y (find_y (line));
- }
- }
-
- void
- goproz (number)
- int number;
- {
- goline (number * total_lines / 100);
- }
-
- void
- GOTO ()
- {
- uchar c;
- char end;
- int number;
-
- if (! char_ready_within (500)) status_msg ("HOP ... command (fortified) or line number...");
- if (quit == TRUE) return;
- c = readchar ();
- if (quit == TRUE) return;
- if ('0' <= c && c <= '9') {
- end = get_number ("Please continue line number...", c, & number);
- if (end == '%')
- goproz (number);
- else if (end != ERRORS)
- goline (number);
- return;
- }
- else {
- clear_status ();
- hop_flag = 1;
- (* key_map [c]) (c);
- return;
- }
- }
-
- /*
- * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes
- * top_line of display.) Try to leave the cursor on the same line. If this is
- * not possible, leave cursor on the line halfway the page.
- */
- void
- PD ()
- {
- register int i;
- int new_y;
-
- if (hop_flag > 0) {hop_flag = 0; EFILE (); return;}
-
- for (i = 0; i < SCREENMAX; i ++)
- if (forward_scroll (page_scroll) == ERRORS)
- break; /* EOF reached */
-
- if (y - i < 0) /* Line no longer on screen */
- new_y = (page_stay == TRUE) ? 0 : SCREENMAX >> 1;
- else new_y = y - i;
-
- if (page_scroll == FALSE) display (0, top_line, last_y, new_y);
-
- move_y (new_y);
- }
-
- /*
- * Scroll backwards one page or to top of file, whatever comes first.
- * (Top_line becomes bot_line of display).
- * The very bottom line (YMAX) is always blank.
- * Try to leave the cursor on the same line.
- * If this is not possible, leave cursor on the line halfway the page.
- */
- void
- PU ()
- {
- register int i;
- int new_y;
-
- if (hop_flag > 0) {hop_flag = 0; BFILE (); return;}
-
- for (i = 0; i < SCREENMAX; i ++) {
- if (reverse_scroll (page_scroll) == ERRORS)
- /* should also flag reverse_scroll that clearing of
- bottom line is not desired */
- break; /* Top of file reached */
- }
-
- if (y + i > SCREENMAX) /* line no longer on screen */
- new_y = (page_stay == TRUE) ? last_y : SCREENMAX >> 1;
- else
- new_y = y + i;
-
- if (can_scroll_reverse == TRUE && page_scroll == TRUE) {
- set_cursor (0, YMAX); /* Erase very bottom line */
- clear_lastline ();
- }
- else display (0, top_line, last_y, new_y);
-
- move_y (new_y);
- }
-
- /*
- * Go to top of file, scrolling if possible, else redrawing screen.
- */
- void
- BFILE ()
- {
- if (proceed (top_line, - SCREENMAX) == header)
- PU (); /* It fits. Let PU do it */
- else {
- reset (header->next, 0);/* Reset top_line, etc. */
- RD_y (0); /* Display full page */
- }
- move_to (LINE_START, 0);
- }
-
- /*
- * Go to last position of text, scrolling if possible, else redrawing screen
- */
- void
- EFILE ()
- {
- /* if (tail->prev->text [0] != '\n') dummy_line (); */
- if (proceed (bot_line, SCREENMAX) == tail)
- PD (); /* It fits. Let PD do it */
- else {
- reset (proceed (tail->prev, - SCREENMAX), SCREENMAX);
- RD_y (last_y); /* Display full page */
- }
- move_to (LINE_END /* not START ("EFILE"!) */, last_y);
- }
-
- /*
- * Scroll one line up. Leave the cursor on the same line (if possible).
- */
- void
- SU ()
- {
- register int i;
-
- if (hop_flag > 0) {
- hop_flag = 0;
- for (i = 0; i < (SCREENMAX >> 1); i ++) SU ();
- return;
- }
-
- if (reverse_scroll (TRUE) != ERRORS) { /* else we are at top of file */
- move_y ((y == SCREENMAX) ? SCREENMAX : y + 1);
- }
- }
-
- /*
- * Scroll one line down. Leave the cursor on the same line (if possible).
- */
- void
- SD ()
- {
- register int i;
-
- if (hop_flag > 0) {
- hop_flag = 0;
- for (i = 0; i < (SCREENMAX >> 1); i ++) SD ();
- return;
- }
-
- if (forward_scroll (TRUE) != ERRORS)
- move_y ((y == 0) ? 0 : y - 1);
- }
-
- /*
- * Perform a forward scroll. It returns ERRORS if we're at the last line of
- * the file.
- */
- int
- forward_scroll (update)
- FLAG update;
- {
- if (bot_line->next == tail) /* Last line of file. No dice */
- return ERRORS;
- top_line = top_line->next;
- bot_line = bot_line->next;
- cur_line = cur_line->next;
-
- /* Perform the scroll on screen */
- if (update == TRUE) {
- scroll_forward ();
- set_cursor (0, SCREENMAX);
- line_print (bot_line);
- }
-
- return FINE;
- }
-
- /*
- * Perform a backwards scroll. It returns ERRORS if we're at the first line
- * of the file. It updates the display completely if update is TRUE.
- * Otherwise it leaves that to the caller (page up function).
- */
- int
- reverse_scroll (update)
- FLAG update;
- {
- if (top_line->prev == header)
- return ERRORS; /* Top of file. Can't scroll */
-
- if (last_y != SCREENMAX) /* Reset last_y if necessary */
- last_y ++;
- else
- bot_line = bot_line->prev; /* Else adjust bot_line */
- top_line = top_line->prev;
- cur_line = cur_line->prev;
-
- /* Perform the scroll on screen */
- if (update == TRUE)
- if (can_scroll_reverse == TRUE) {
- set_cursor (0, 0);
- scroll_reverse ();
- set_cursor (0, YMAX); /* Erase very bottom line */
- clear_lastline ();
- set_cursor (0, 0);
- line_print (top_line);
- }
- else display (0, top_line, last_y, y);
-
- return FINE;
- }
-
- /*----------------------*
- * Word moves *
- *----------------------*/
-
- /*
- * A word was previously defined as a number of non-blank characters
- * separated by tabs, spaces or linefeeds.
- * By consulting idfchar (), sequences of real letters only or digits
- * or underlines are recognized as words.
- */
- extern int idfchar ();
-
- /*
- * BSEN () and ESEN () look for the beginning or end of the current sentence.
- */
- void
- BSEN ()
- {
- search_for ("[;.]", REVERSE);
- }
-
- void
- ESEN ()
- {
- search_for ("[;.]", FORWARD);
- }
-
- /*
- * SIDF () searches for the identifier at the current position
- */
- void
- SIDF (method)
- FLAG method;
- {
- char idf_buf [MAX_CHARS]; /* identifier to search for */
- char * idf_buf_poi = idf_buf;
- char * idf_poi;
-
- if (! alpha (* cur_text)) {
- error ("No identifier", NIL_PTR);
- return;
- } else {
- idf_poi = cur_text;
- while (alpha (* idf_poi) && idf_poi != cur_line->text) idf_poi --;
- if (! alpha (* idf_poi)) idf_poi ++;
- while (alpha (* idf_poi)) * idf_buf_poi ++ = * idf_poi ++;
- * idf_buf_poi = '\0';
- search_for (idf_buf, method);
- }
- }
-
- /*
- * MPW () moves to the start of the previous word. A word is defined as a
- * number of non-blank characters separated by tabs spaces or linefeeds.
- */
- void
- move_previous_word (remove)
- FLAG remove;
- {
- register char * begin_line;
- register char * textp;
- char start_char = * cur_text;
- char * start_pos = cur_text;
- FLAG idfsearch;
-
- if (remove == DELETE && viewonly == TRUE)
- {viewonlyerr (); return;}
-
- /* First check if we're at the beginning of line. */
- if (cur_text == cur_line->text) {
- if (cur_line->prev == header)
- return;
- start_char = '\0';
- }
-
- MLF ();
-
- begin_line = cur_line->text;
- textp = cur_text;
-
- /* Check if we're in the middle of a word. */
- if (!alpha (* textp) || !alpha (start_char)) {
- while (textp != begin_line && (white_space (* textp) || * textp == '\n'))
- textp --;
- }
-
- /* Now we're at the end of previous word. Skip non-blanks until a blank comes */
- if (idfchar (* textp)) {
- idfsearch = TRUE;
- while (textp != begin_line && idfchar (* textp))
- textp --;
- }
- else {
- idfsearch = FALSE;
- while (textp != begin_line && alpha (* textp) && !idfchar (* textp))
- textp --;
- }
-
- /* Go to the next char if we're not at the beginning of the line */
- /* At the beginning of the line, check whether to stay or to go to the word */
- if (textp != begin_line && * textp != '\n')
- textp ++;
- else if (textp == begin_line && * textp != '\n' &&
- ((idfsearch == TRUE) ? !idfchar (* textp)
- /* : white_space (* textp) */
- : (!alpha (* textp) || idfchar (* textp)))) {
- textp ++;
- if (white_space (* textp) || textp == start_pos)
- /* no word there or not moved, so go back */
- textp --;
- }
-
- /* Find the x-coordinate of this address, and move to it */
- move_address (textp, y);
- if (remove == DELETE)
- (void) delete_text (cur_line, textp, cur_line, start_pos);
- }
-
- void
- MPW ()
- {
- if (hop_flag > 0) BSEN ();
- else move_previous_word (NO_DELETE);
- }
-
- /*
- * MNW () moves to the start of the next word. A word is defined as a number of
- * non-blank characters separated by tabs spaces or linefeeds. Always keep in
- * mind that the pointer shouldn't pass the '\n'.
- */
- void
- move_next_word (remove)
- FLAG remove;
- {
- register char * textp = cur_text;
-
- if (remove == DELETE && viewonly == TRUE)
- {viewonlyerr (); return;}
-
- /* Move to the end of the current word. */
- if (idfchar (* textp))
- while (* textp != '\n' && idfchar (* textp))
- textp ++;
- else
- while (alpha (* textp) && !idfchar (* textp))
- textp ++;
-
- /* Skip all white spaces */
- while (* textp != '\n' && white_space (* textp))
- textp ++;
- /* If we're deleting, delete the text in between */
- if (remove == DELETE) {
- (void) delete_text (cur_line, cur_text, cur_line, textp);
- return;
- }
-
- /* If we're at end of line, move to the beginning of (first word on) the next line */
- if (* textp == '\n' && cur_line->next != tail) {
- MDN ();
- move_to (LINE_START, y);
- textp = cur_text;
- /* while (* textp != '\n' && white_space (* textp)) */
- /* textp ++; */
- }
- move_address (textp, y);
- }
-
- void
- MNW ()
- {
- if (hop_flag > 0) ESEN ();
- else move_next_word (NO_DELETE);
- }
-
- /*
- * find_y () checks if the matched line is on the current page. If it is, it
- * returns the new y coordinate, else it displays the correct page with the
- * matched line in the middle and returns the new y value;
- */
- int
- find_y_RD (match_line, redrawflag)
- LINE * match_line;
- FLAG redrawflag;
- {
- register LINE * line;
- register int count = 0;
-
- /* Check if match_line is on the same page as currently displayed. */
- for (line = top_line; line != match_line && line != bot_line->next;
- line = line->next)
- count ++;
- if (line != bot_line->next)
- return count;
-
- /* Display new page, with match_line in center. */
- if ((line = proceed (match_line, - (SCREENMAX >> 1))) == header) {
- /* Can't display in the middle. Make first line of file top_line */
- count = 0;
- for (line = header->next; line != match_line; line = line->next)
- count ++;
- line = header->next;
- }
- else /* New page is displayed. Set cursor to middle of page */
- count = SCREENMAX >> 1;
-
- /* Reset pointers and redraw the screen */
- reset (line, 0);
- if (redrawflag == TRUE) RD_y (count);
-
- return count;
- }
-
- int
- find_y (match_line)
- LINE * match_line;
- {
- return find_y_RD (match_line, TRUE);
- }
-
- int
- find_y_w_o_RD (match_line)
- LINE * match_line;
- {
- return find_y_RD (match_line, FALSE);
- }
-
- /*
- * Dummy_line () adds an empty line at the end of the file. This is
- * sometimes useful in combination with the EFILE and MDN command in
- * combination with the Yank command set.
- * !!! I see no use for this and I don't consider such autonomous
- * !!! modifications of the text (without user request) acceptable. TW.
- */
- #ifdef UNUSED
- void
- dummy_line ()
- {
- (void) line_insert (tail->prev, "\n", 1);
- tail->prev->shift_count = DUMMY;
- if (last_y != SCREENMAX) {
- last_y ++;
- bot_line = bot_line->next;
- }
- }
- #endif /* UNUSED */
-
- /* ================================================================== *
- * Modify Commands *
- * ================================================================== */
-
- /*
- * DCC deletes the character under the cursor. If this character is a '\n' the
- * current line is joined with the next one.
- * If this character is the only character of the line, the current line will
- * be deleted.
- */
- void
- DCC ()
- {
- if (* cur_text == '\n')
- if (cur_line->next == tail)
- return;
- else
- (void) delete_text (cur_line, cur_text, cur_line->next, cur_line->next->text);
- else {
- if (Chinese == TRUE && multichar (* cur_text))
- (void) delete_text (cur_line, cur_text, cur_line, cur_text + 2);
- else
- (void) delete_text (cur_line, cur_text, cur_line, cur_text + 1);
- }
- }
-
- /*
- * DPC deletes the character on the left side of the cursor. If the cursor
- * is at the beginning of the line, the last character if the previous line
- * is deleted. With hop flag, delete left part of line from current point.
- */
- void
- DPC ()
- {
- char * delete_pos;
-
- if (x == 0 && cur_line->prev == header)
- return; /* Top of file */
-
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- if (hop_flag > 0) {
- hop_flag = 0;
- if (cur_text != cur_line->text) {
- delete_pos = cur_text;
- BLINE ();
- (void) delete_text (cur_line, cur_line->text, cur_line, delete_pos);
- }
- }
- else {
- MLF (); /* Move one left */
- DCC (); /* Delete character under cursor */
- }
- }
-
- /*
- * DLINE delete the whole current line.
- */
- void
- DLINE ()
- {
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- if (hop_flag > 0) {
- hop_flag = 0;
- if (* cur_text != '\n')
- (void) delete_text (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
- }
- else {
- (void) delete_text (cur_line, cur_line->text, cur_line->next, cur_line->next->text);
- BLINE ();
- }
- }
-
- /*
- * DLN deletes all characters until the end of the line. If the current
- * character is a '\n', then delete that char.
- */
- void
- DLN ()
- {
- if (* cur_text == '\n')
- DCC ();
- else if (hop_flag > 0) {
- hop_flag = 0;
- DLINE ();
- }
- else (void) delete_text (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
- }
-
- /*
- * DNW () deletes the next word (as defined in MNW ())
- */
- void
- DNW ()
- {
- if (* cur_text == '\n')
- DCC ();
- else
- move_next_word (DELETE);
- }
-
- /*
- * DPW () deletes the previous word (as defined in MPW ())
- */
- void
- DPW ()
- {
- if (cur_text == cur_line->text)
- DPC ();
- else
- move_previous_word (DELETE);
- }
-
- /*
- * Insert character `character' at current location.
- */
- void
- SNL ()
- {
- S ('\n');
- }
-
- void
- S (character)
- register uchar character;
- {
- static uchar buffer [3];
- static uchar firstbyte;
- static int width = 1;
-
- if (Chinese == TRUE) {
- if (firstbyte != '\0') {
- buffer [0] = firstbyte;
- buffer [1] = character;
- width = 2;
- } else if (multichar (character)) {
- firstbyte = character;
- return;
- } else {
- buffer [0] = character;
- buffer [1] = '\0';
- width = 1;
- }
- firstbyte = '\0';
- } else
- buffer [0] = character;
-
- /* Insert the character */
- if (insert (cur_line, cur_text, buffer) == ERRORS)
- return;
-
- /* Fix screen */
- if (character == '\n') {
- set_cursor (0, y);
- if (y == SCREENMAX) { /* Can't use display () */
- line_print (cur_line);
- (void) forward_scroll (TRUE);
- move_to (0, y);
- }
- else {
- reset (top_line, y); /* Reset pointers */
- if (can_add_line == TRUE) {
- add_line (y + 1);
- clear_status ();
- display (y, cur_line, 1, y + 1);
- }
- else display (y, cur_line, last_y - y, y + 1);
- move_to (0, y + 1);
- }
- }
- else if (x + width == XBREAK) /* If line must be shifted, just call move_to */
- move_to (x + width, y);
- else { /* else display rest of line */
- put_line (cur_line, x, FALSE, FALSE);
- move_to (x + width, y);
- }
- }
-
- /*
- * Replace current character with its hex representation.
- */
- uchar hexdig (c)
- uchar c;
- {
- if (c < 10) return c + '0';
- else return c - 10 + 'A';
- }
- void
- insertcode (c, radix)
- uchar c;
- int radix;
- {
- int radix2;
-
- if (radix == 8) {
- S (hexdig ((c >> 6) & 007));
- S (hexdig ((c >> 3) & 007));
- S (hexdig ((c) & 007));
- } else if (radix == 16) {
- S (hexdig ((c >> 4) & 017));
- S (hexdig ((c) & 017));
- } else { /* assume radix = 10 or, at least, three digits suffice */
- radix2 = radix * radix;
- S (hexdig (c / radix2));
- S (hexdig ((c % radix2) / radix));
- S (hexdig (c % radix));
- }
- }
- void
- changetocode (radix)
- int radix;
- {
- uchar c = * cur_text;
-
- if (c == '\n') {
- #ifdef msdos
- insertcode ('\r', radix);
- #endif
- insertcode ('\n', radix);
- } else {
- DCC ();
- insertcode (c, radix);
- }
- }
-
- /*
- * insert_accent inserts accented character
- */
- void
- insert_accent (name, routine)
- char * name;
- uchar (* routine) ();
- {
- register uchar letter;
-
- build_string (text_buffer, "Enter character to place %s on...", name);
- status_msg (text_buffer);
- letter = (* routine) (readchar ());
- clear_status ();
- S (letter);
- }
-
- /*
- * CTRl inserts a control-char at the current location. A message that this
- * function is called is displayed at the status line.
- */
- void
- CTRl ()
- {
- register uchar ctrl;
-
- status_msg ("Enter control character (or accent)...");
- ctrl = readchar ();
- if (ctrl == ring || ctrl == ',')
- {insert_accent ("angstrom/cedilla", angstrom); return;}
- else switch (ctrl) {
- case '"': {insert_accent ("diaeresis", diaeresis); return;}
- case '\'': {insert_accent ("acute (d'aigu)", acute); return;}
- case '`': {insert_accent ("grave", grave); return;}
- case '^': {insert_accent ("circumflex", circumflex); return;}
- case '~': {insert_accent ("tilde", tilde); return;}
- }
- clear_status ();
- if ((ctrl == '\177') || (ctrl == '?')) {S ('\177'); return;}
- ctrl = ctrl & '\237';
- if (ctrl == '\0') error ("Can't handle NULL char - not inserted", NIL_PTR);
- else S (ctrl);
- }
-
- /*
- * LIB insert a line at the current position and moves back to the end of
- * the previous line.
- */
- void
- LIB ()
- {
- hop_flag = 0;
-
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- S ('\n'); /* Insert the line */
- MUP (); /* Move one line up */
- move_to (LINE_END, y); /* Move to end of this line */
- }
-
- /* ================================================================== *
- * Yank Commands *
- * ================================================================== */
-
- LINE * mark_line = NIL_LINE; /* For marking position. */
- char * mark_text = NIL_PTR;
- LINE * mark_n_line [10] = {NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE,
- NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE, NIL_LINE};
- char * mark_n_text [10] = {NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR,
- NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR, NIL_PTR};
- int lines_saved; /* Nr of lines in buffer */
-
- /*
- * PT () inserts the buffer at the current location.
- */
- void
- PT ()
- {
- register int fd; /* File descriptor for buffer */
-
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- if (hop_flag > 0) {
- if ((fd = open (yankie_file, O_RDONLY | O_BINARY, 0)) < 0) {
- error ("No inter window buffer present", NIL_PTR);
- return;
- }
- }
- else if ((fd = scratch_file (READ, FALSE)) == ERRORS) {
- error ("Buffer is empty", NIL_PTR);
- return;
- }
- /* Insert the buffer */
- /* file_insert (fd, FALSE); => positioning error when TAB in last line */
- file_insert (fd, TRUE);
- }
-
- /*
- * INSFILE () prompt for a filename and inserts the file at the current location
- * in the file.
- */
- void
- INSFILE ()
- {
- register int fd; /* File descriptor of file */
- char name [maxLINE_LEN]; /* Buffer for file name */
-
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- /* Get the file name */
- if (get_file ("Get and insert file:", name) != FINE)
- return;
- clear_status ();
-
- if ((fd = open (name, O_RDONLY | O_BINARY, 0)) < 0)
- error ("Cannot open file: " /*, name */, serror ());
- else { /* Insert the file */
- file_insert (fd, TRUE); /* leave cursor at begin of insertion */
- }
- }
-
- /*
- * File_insert () inserts the contents of an opened file (as given by
- * filedescriptor fd) at the current location.
- * After the insertion, if old_pos is TRUE, the cursor remains at the
- * start of the inserted text, if old_pos is FALSE, it is placed to
- * its end. If old_pos is FALSE, this works erroneously if the last line
- * inserted contains a TAB character!!
- */
- void
- file_insert (fd, old_pos)
- int fd;
- FLAG old_pos;
- {
- char line_buffer [MAX_CHARS]; /* Buffer for next line */
- register LINE * line = cur_line;
- register int line_count = total_lines; /* Nr of lines inserted */
- LINE * page = cur_line;
- int ret = ERRORS;
-
- get_l_err1 = NIL_PTR;
- get_l_err2 = NIL_PTR;
-
- /* Get the first piece of text (might be ended with a '\n') from fd */
- if (get_line (fd, line_buffer) == ERRORS)
- return; /* Empty file */
-
- /* Insert this text at the current location */
- if (insert (line, cur_text, line_buffer) == ERRORS)
- return;
-
- /* Repeat getting lines (and inserting lines) until EOF is reached */
- while (line != NIL_LINE
- && (ret = get_line (fd, line_buffer)) != ERRORS && ret != NO_LINE)
- line = line_insert (line, line_buffer, ret);
-
- if (line == NIL_LINE) sleep (2) /* show memory allocation error msg */;
- else if (ret == NO_LINE) { /* Last line read not ended by a '\n' */
- line = line->next;
- if (insert (line, line->text, line_buffer) == ERRORS)
- sleep (2) /* give time to read error msg */;
- }
-
- (void) close (fd);
-
- /* If illegal lines were input, report */
- if ((get_l_err1 != NIL_PTR) || (get_l_err2 != NIL_PTR)) {
- ring_bell ();
- error (get_l_err1, get_l_err2);
- sleep (1);
- }
-
- /* Calculate nr of lines added */
- line_count = total_lines - line_count;
-
- /* Fix the screen */
- if (line_count == 0) { /* Only one line changed */
- set_cursor (0, y);
- line_print (line);
- move_to ((old_pos == TRUE) ? x : x + length_of (line_buffer), y);
- }
- else { /* Several lines changed */
- reset (top_line, y); /* Reset pointers */
- while (page != line && page != bot_line->next)
- page = page->next;
- if (page != bot_line->next || old_pos == TRUE)
- display (y, cur_line, SCREENMAX - y, y);
- /* screen display style parameter (last) may be inaccurate */
- if (old_pos == TRUE)
- move_to (x, y);
- else if (ret == NO_LINE)
- move_to (length_of (line_buffer), find_y (line));
- else
- move_to (0, find_y (line->next));
- }
-
- /* If nr of added line >= REPORT, print the count */
- if (line_count >= REPORT)
- status_line (num_out ((long) line_count), " lines added");
- }
-
- /*
- * WB () writes the buffer (yank_file) into another file, which
- * is prompted for.
- */
- void
- WB ()
- {
- register int new_fd; /* Filedescriptor to copy file */
- int yank_fd; /* Filedescriptor to buffer */
- register int cnt; /* Count check for read/write */
- int ret = FINE; /* Error check for write */
- char file_name [maxLINE_LEN]; /* Output file name */
- char * msg_doing; char * msg_done;
-
- /* Checkout the buffer */
- if ((yank_fd = scratch_file (READ, FALSE)) == ERRORS) {
- error ("Buffer is empty", NIL_PTR);
- return;
- }
-
- /* Get file name */
- if (get_file ((hop_flag > 0) ? "Append buffer to file:"
- : "Write buffer to file:", file_name) != FINE)
- return;
-
- /* Create the new file or open previous file for appending */
- if (hop_flag > 0) {
- if ((new_fd = open (file_name, O_WRONLY | O_CREAT | O_APPEND | O_BINARY, fprot)) < 0) {
- error ("Cannot append to file: ", serror ());
- return;
- }
- msg_doing = "Appending "; msg_done = "Appended";
- }
- else {
- if (checkoverwrite (file_name) != TRUE)
- return;
- else if ((new_fd = open (file_name, O_WRONLY | O_CREAT | O_BINARY, fprot)) < 0) {
- error ("Cannot create file: ", serror ());
- return;
- }
- msg_doing = "Writing "; msg_done = "Wrote";
- }
-
- status_line (msg_doing, file_name);
-
- /* Copy buffer into file */
- while ((cnt = read (yank_fd, text_buffer, sizeof (text_buffer))) > 0)
- if (write (new_fd, text_buffer, cnt) != cnt) {
- bad_write (new_fd);
- ret = ERRORS;
- break;
- }
-
- /* Clean up open files and status_line */
- (void) close (new_fd);
- (void) close (yank_fd);
-
- if (ret != ERRORS) /* Bad write */
- file_status (msg_done, chars_saved, file_name, lines_saved,
- FALSE, TRUE, FALSE, FALSE);
- }
-
- /*
- * MARK sets mark_line / mark_text to the current line / current text pointer.
- */
- void
- MARK ()
- {
- if (hop_flag > 0) GOMA ();
- else {
- mark_line = cur_line;
- mark_text = cur_text;
- status_msg ("Mark set");
- }
- }
-
- /*
- * GOMA moves to the marked position
- */
- void
- GOMA ()
- {
- if (checkmark (mark_line, mark_text) == NOT_VALID)
- error ("Mark not set", NIL_PTR);
- else
- move_address (mark_text, find_y (mark_line));
- }
-
- /*
- * MARKn sets mark n to the current line / current text pointer.
- */
- void
- MARKn (c)
- uchar c;
- {
- if (hop_flag > 0) GOMAn (c);
- else {
- mark_n_line [c & '\17'] = cur_line;
- mark_n_text [c & '\17'] = cur_text;
- status_msg ("Mark set");
- }
- }
-
- /*
- * GOMAn moves to the marked position n
- */
- void
- GOMAn (c)
- uchar c;
- {
- if (checkmark (mark_n_line [c & '\17'], mark_n_text [c & '\17']) == NOT_VALID)
- error ("Mark not set", NIL_PTR);
- else
- move_address (mark_n_text [c & '\17'], find_y (mark_n_line [c & '\17']));
- }
-
- /*
- * Yankie () provides a reference to the last saved buffer to be read
- * by other mined invocations.
- */
- void
- yankie ()
- {
- delete_file (yankie_file);
- #ifdef unix
- link (yank_file, yankie_file);
- #else
- build_string (text_buffer, copycommand, yank_file, yankie_file);
- system (text_buffer);
- #endif
- }
-
- /*
- * Set_up is an interface to the actual yank. It calls checkmark () to check
- * if the marked position is still valid. If it is, yank is called with the
- * arguments in the right order.
- */
- void
- set_up (remove, append)
- FLAG remove; /* == DELETE if text should be deleted */
- FLAG append; /* == TRUE if text should only be appended to yank buffer */
- {
- switch (checkmark (mark_line, mark_text)) {
- case NOT_VALID :
- error ("Mark not set", NIL_PTR);
- return;
- case SMALLER :
- yank (mark_line, mark_text, cur_line, cur_text, remove, append);
- yankie ();
- break;
- case BIGGER :
- yank (cur_line, cur_text, mark_line, mark_text, remove, append);
- yankie ();
- break;
- case SAME : /* Ignore stupid behaviour */
- /* yank_status = EMPTY; */
- chars_saved = 0L;
- status_msg ("Nothing to save");
- break;
- default :
- error ("Internal mark error", NIL_PTR);
- return;
- }
- }
-
- /*
- * YA () puts the text between the marked position and the current
- * in the buffer.
- */
- void
- YA ()
- {
- set_up (NO_DELETE, (hop_flag > 0) ? TRUE : FALSE);
- }
-
- /*
- * DT () is essentially the same as YA (), but in DT () the text is deleted.
- */
- void
- DT ()
- {
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- set_up (DELETE, (hop_flag > 0) ? TRUE : FALSE);
- }
-
- /*
- * Check_mark () checks if mark_line and mark_text are still valid pointers.
- * If they are it returns
- * SMALLER if the marked position is before the current,
- * BIGGER if it isn't or SAME if somebody didn't get the point.
- * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
- * Legal () checks if mark_text is valid on the mark_line.
- */
- FLAG
- checkmark (mark_line, mark_text)
- register LINE * mark_line;
- register char * mark_text;
- {
- register LINE * line;
- FLAG cur_seen = FALSE;
-
- /* Special case: check is mark_line and cur_line are the same. */
- if (mark_line == cur_line) {
- if (mark_text == cur_text) /* Even same place */
- return SAME;
- if (legal (mark_line, mark_text) == ERRORS) /* mark_text out of range */
- return NOT_VALID;
- return (mark_text < cur_text) ? SMALLER : BIGGER;
- }
-
- /* Start looking for mark_line in the line structure */
- for (line = header->next; line != tail; line = line->next) {
- if (line == cur_line)
- cur_seen = TRUE;
- else if (line == mark_line)
- break;
- }
-
- /* If we found mark_line (line != tail) check for legality of mark_text */
- if (line == tail || legal (mark_line, mark_text) == ERRORS)
- return NOT_VALID;
-
- /* cur_seen is TRUE if cur_line is before mark_line */
- return (cur_seen == TRUE) ? BIGGER : SMALLER;
- }
-
- /*
- * Legal () checks if mark_text is still a valid pointer.
- */
- int
- legal (mark_line, mark_text)
- register LINE * mark_line;
- register char * mark_text;
- {
- register char * textp = mark_line->text;
-
- /* Locate mark_text on mark_line */
- while (textp != mark_text && * textp != '\0')
- textp ++;
- return (* textp == '\0') ? ERRORS : FINE;
- }
-
- /*
- * Yank puts all the text between start_position and end_position into
- * the buffer.
- * The caller must check that the arguments to yank () are valid (e.g. in
- * the right order).
- */
- void
- yank (start_line, start_textp, end_line, end_textp, remove, append)
- LINE * start_line, * end_line;
- char * start_textp, * end_textp;
- FLAG remove; /* == DELETE if text should be deleted */
- FLAG append; /* == TRUE if text should only be appended to yank buffer */
- {
- register LINE * line = start_line;
- register char * textp = start_textp;
- int fd;
-
- /* Create file to hold buffer */
- if ((fd = scratch_file (WRITE, append)) == ERRORS)
- return;
-
- chars_saved = 0L;
- lines_saved = 0;
- if (append == TRUE)
- status_msg ("Appending text ...");
- else status_msg ("Saving text ...");
-
- /* Keep writing chars until the end_location is reached. */
- while (textp != end_textp) {
- if (writechar (fd, * textp) == ERRORS) {
- (void) close (fd);
- return;
- }
- if (* textp ++ == '\n') { /* Move to the next line */
- line = line->next;
- textp = line->text;
- lines_saved ++;
- }
- chars_saved ++;
- }
-
- /* Flush the I/O buffer and close file */
- if (flush_buffer (fd) == ERRORS) {
- (void) close (fd);
- return;
- }
- (void) close (fd);
- yank_status = VALID;
-
- /*
- * Check if the text should be deleted as well. In case it should,
- * the following hack is used to save a lot of code.
- * First move back to the start_position (this might be the current
- * location) and then delete the text.
- * This might look a bit confusing to the user the first time.
- * Delete () will fix the screen.
- */
- if (remove == DELETE) {
- move_to (find_x (start_line, start_textp), find_y (start_line));
- if (delete_text (start_line, start_textp, end_line, end_textp)
- == ERRORS) {
- sleep (2) /* give time to read allocation error msg */;
- }
- mark_line = cur_line;
- mark_text = cur_text;
- }
-
- if (append == TRUE)
- status_line (num_out (chars_saved), " characters appended to buffer");
- else status_line (num_out (chars_saved), " characters saved in buffer");
- }
-
- /*
- * Scratch_file () tries to create a unique file in a temporary directory.
- * It tries several different filenames until one can be created
- * or MAXTRIALS attempts have been made.
- * After MAXTRIALS times, an error message is given and ERRORS is returned.
- */
-
- #define MAXTRIALS 99
-
- int
- scratch_file (mode, append)
- FLAG mode; /* Can be READ or WRITE permission */
- FLAG append; /* == TRUE if text should only be appended to yank buffer */
- {
- static int trials = 0; /* Keep track of trials */
- int fd = 0; /* Filedescriptor to buffer */
-
- /* If yank_status == NOT_VALID, scratch_file is called for the first time */
- if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */
- /* Generate file name. */
- #ifdef msdos
- build_string (yank_file, "%s%d", yankie_file, trials);
- #else
- build_string (yank_file, "%s%d-%d", yankie_file, getpid (), trials);
- #endif
- /* Check file existence */
- if (access (yank_file, 0 /* F_OK */) == 0
- || (fd = creat (yank_file, bufprot)) < 0) {
- if (++ trials >= MAXTRIALS) {
- build_string (text_buffer, "Unable to create scratchfile %s: ", yank_file);
- if (fd == 0)
- error (text_buffer, "File exists");
- else
- error (text_buffer, serror ());
- return ERRORS;
- }
- else
- return scratch_file (mode, append); /* try again */
- }
- }
- else if (yank_status == NOT_VALID && mode == READ) {
- return ERRORS;
- }
- else /* yank_status == VALID */
- if ( (mode == READ && (fd = open (yank_file, O_RDONLY | O_BINARY, 0)) < 0)
- || (mode == WRITE &&
- (fd = open (yank_file, O_WRONLY | O_CREAT |
- ((append == TRUE) ? O_APPEND : O_TRUNC), bufprot)) < 0)) {
- yank_status = NOT_VALID;
- return ERRORS;
- }
-
- clear_buffer ();
- return fd;
- }
-
- /* ================================================================== *
- * Search Commands *
- * ================================================================== */
-
- /*
- * A regular expression consists of a sequence of:
- * 1. A normal character matching that character.
- * 2. A . matching any character.
- * 3. A ^ matching the begin of a line.
- * 4. A $ (as last character of the pattern) mathing the end of a line.
- * 5. A \<character> matching <character>.
- * 6. A number of characters enclosed in [] pairs matching any of these
- * characters. A list of characters can be indicated by a '-'. So
- * [a-z] matches any letter of the alphabet. If the first character
- * after the '[' is a '^' then the set is negated (matching none of
- * the characters).
- * A ']', '^' or '-' can be escaped by putting a '\' in front of it.
- * 7. If one of the expressions as described in 1-6 is followed by a
- * '*' than that expressions matches a sequence of 0 or more of
- * that expression.
- */
-
- char typed_expression [maxLINE_LEN]; /* Holds previous search expression */
-
- /*
- * SFW searches forward for an expression.
- */
- void
- SFW ()
- {
- if (hop_flag > 0) SIDF (FORWARD);
- else search ("Search forward:", FORWARD);
- }
-
- /*
- * SRV searches backwards for an expression.
- */
- void
- SRV ()
- {
- if (hop_flag > 0) SIDF (REVERSE);
- else search ("Search reverse:", REVERSE);
- }
-
- /*
- * RS searches using the last search direction and expression.
- */
- void
- RS ()
- {
- if (hop_flag > 0) prev_search ();
- else re_search ();
- }
-
- /*
- * Get_expression () prompts for an expression. If just a return is typed, the
- * old expression is used. If the expression changed, compile () is called and
- * the returning REGEX structure is returned. It returns NIL_REG upon error.
- * The save flag indicates whether the expression should be appended at the
- * message pointer.
- */
- char exp_buf [maxLINE_LEN]; /* Buffer for new expr. */
-
- REGEX *
- get_expression (message)
- char * message;
- {
- static REGEX program; /* Program of expression */
-
- if (get_string (message, exp_buf, FALSE) == ERRORS)
- return NIL_REG;
-
- if (exp_buf [0] == '\0' && typed_expression [0] == '\0') {
- error ("No previous search expression", NIL_PTR);
- return NIL_REG;
- }
-
- if (exp_buf [0] != '\0') { /* A new expr. is typed */
- copy_string (typed_expression, exp_buf); /* Save expr. */
- /* Compile new expression: */
- if (compile (exp_buf, & program) == ERRORS)
- return NIL_REG;
- }
-
- if (program.status == REG_ERROR) { /* Error during compiling */
- error (program.result.err_mess, NIL_PTR);
- return NIL_REG;
- }
- return & program;
- }
-
- /*
- * make_expression is only called by search_for and maintains its own,
- * independant search program buffer
- */
- REGEX *
- make_expression (expr)
- char * expr;
- {
- static REGEX program; /* Program of expression */
-
- if (compile (expr, & program) == ERRORS) /* Compile new expression */
- return NIL_REG;
-
- if (program.status == REG_ERROR) { /* Error during compiling */
- error (program.result.err_mess, NIL_PTR);
- return NIL_REG;
- }
- return & program;
- }
-
- /*
- * Change () prompts for an expression and a substitution pattern and changes
- * all matches of the expression into the substitution.
- * change () starts looking for expressions at the current line and
- * continues until the end of the file if the FLAG `global' is VALID.
- * It prompts for each change if the FLAG `confirm' is TRUE.
- * For a call graph of search procedures see search ().
- */
- void
- change (message, global, confirm)
- char * message; /* Message to prompt for expression */
- FLAG global, confirm;
- {
- char mess_buf [maxLINE_LEN]; /* Buffer to hold message */
- char replacement [maxLINE_LEN]; /* Buffer to hold subst. pattern */
- REGEX * program; /* Program resulting from compilation */
- register LINE * line = cur_line;
- register char * textp;
- char * substitute ();
- long lines = 0L; /* Nr of lines on which subs occurred */
- long subs = 0L; /* Nr of subs made */
- int ly = y; /* Index to check if line is on screen */
- int previousy = y;
- char c;
- FLAG quit_change;
-
- if (viewonly == TRUE)
- {viewonlyerr (); return;}
-
- /* Save message and get expression */
- if ((program = get_expression (message)) == NIL_REG)
- return;
-
- /* Get substitution pattern */
- build_string (mess_buf, "%s %s by:", message, typed_expression);
- if (get_string (mess_buf, replacement, FALSE) == ERRORS)
- return;
-
- set_cursor (0, YMAX);
- flush ();
- /* Substitute until end of file */
- do {
- if (line_check (program, line->text, FORWARD)) {
- lines ++;
- /* Repeat sub. on this line as long as we find a match */
- do {
- if (confirm == TRUE) {
- ly = find_y (line);
- textp = program->start_ptr;
- move_address (program->start_ptr, ly);
- status_msg ("Replace ? (y/n)");
- c = promptyn ();
- clear_status ();
- if (c == 'y') {
- subs ++; /* Increment subs */
- if ((textp = substitute (line, program, replacement))
- == NIL_PTR)
- return; /* Line too long */
- set_cursor (0, ly);
- line_print (line);
- }
- else
- textp ++;
- }
- else {
- subs ++; /* Increment subs */
- line->shift_count = 0;
- /* in case line would get completely shifted out */
- if ((textp = substitute (line, program, replacement))
- == NIL_PTR) {
- set_cursor (0, ly);
- line_print (line);
- move_to (x, y);
- return; /* Line too long */
- }
- }
- } while ( (program->status & BEGIN_LINE) != BEGIN_LINE
- && (program->status & END_LINE) != END_LINE
- && line_check (program, textp, FORWARD)
- && quit == FALSE);
- /* Check to see if we can print the result */
- if (confirm == FALSE && ly <= SCREENMAX) {
- set_cursor (0, ly);
- line_print (line);
- }
- }
- if (ly <= SCREENMAX)
- ly ++;
- line = line->next;
- } while (line != tail && global == VALID && quit == FALSE);
-
- quit_change = quit;
- /* Fix the status line */
- if (subs == 0L && quit == FALSE)
- error ("Pattern not found", NIL_PTR);
- else if (lines >= REPORT || quit == TRUE) {
- build_string (mess_buf, "%s %ld substitutions on %ld lines",
- (quit_change == TRUE) ? "(Aborted) " : "", subs, lines);
- status_msg (mess_buf);
- }
- else if (global == NOT_VALID && subs >= REPORT)
- status_line (num_out (subs), " substitutions");
- else
- clear_status ();
-
- if (confirm == TRUE) move_to (x, y);
- else move_to (LINE_START, previousy);
-
- /* if (quit == TRUE) swallow_dummy_quit_char (); */
- quit = FALSE;
- }
-
- /*
- * Substitute () replaces the match on this line by the substitute pattern
- * as indicated by the program. Every '&' in the replacement is replaced by
- * the original match. A \ in the replacement escapes the next character.
- */
- char *
- substitute (line, program, replacement)
- LINE * line;
- REGEX * program;
- char * replacement; /* Contains replacement pattern */
- {
- register char * textp = text_buffer;
- register char * subp = replacement;
- char * linep = line->text;
- char * amp;
- char * newtext;
-
- modified = TRUE;
-
- /* Copy part of line until the beginning of the match */
- while (linep != program->start_ptr)
- * textp ++ = * linep ++;
-
- /*
- * Replace the match by the substitution pattern. Each occurrence of '&' is
- * replaced by the original match. A \ escapes the next character.
- */
- while (* subp != '\0' && textp < & text_buffer [MAX_CHARS]) {
- if (* subp == '&') { /* Replace the original match */
- amp = program->start_ptr;
- while (amp < program->end_ptr && textp < & text_buffer [MAX_CHARS])
- * textp ++ = * amp ++;
- subp ++;
- }
- else {
- if (* subp == '\\' && * (subp + 1) != '\0')
- subp ++;
- * textp ++ = * subp ++;
- }
- }
- * textp = '\0';
-
- /* Check for line length not exceeding MAX_CHARS */
- if (length_of (text_buffer) + length_of (program->end_ptr) >= MAX_CHARS) {
- error ("Substitution failed: resulted line too long", NIL_PTR);
- return NIL_PTR;
- }
-
- /* Append last part of line to the newly built line */
- copy_string (textp, program->end_ptr);
-
- /* Free old line and install new one */
- newtext = alloc (length_of (text_buffer) + 1);
- if (newtext == NIL_PTR) {
- ring_bell ();
- error ("Substitution failed: cannot allocate more memory", NIL_PTR);
- return NIL_PTR;
- }
- else {
- free_space (line->text);
- line->text = newtext;
- copy_string (line->text, text_buffer);
- return (line->text + (int) (textp - text_buffer));
- }
- }
-
- /*
- * GR () a replaces all matches from the current position until the end
- * of the file.
- */
- void
- GR ()
- {
- change ("Global replace:", VALID, FALSE);
- }
-
- /*
- * Replace is substitute with confirmation dialogue.
- */
- void
- REPL ()
- {
- change ("Global replace (with confirm):", VALID, TRUE);
- }
-
- /*
- * LR () replaces all matches on the current line.
- */
- void
- LR ()
- {
- change ("Line replace:", NOT_VALID, FALSE);
- }
-
- /*
- * Search () calls get_expression to fetch the expression. If this went well,
- * the function match () is called which returns the line with the next match.
- * If this line is the NIL_LINE, it means that a match could not be found.
- * Find_x () and find_y () display the right page on the screen, and return
- * the right coordinates for x and y.
- * These coordinates are passed to move_to ().
- * Re_search () searches using the last search program and direction.
- Call graph of search procedures:
- RS ------------\
- SFW -\ > re_search > match -----------\
- SRV --> search < \
- \ \
- > get_expression > compile > line_check > check_string
- GR \ / /
- REPL > change <--------------------------------/
- LR / \
- \ substitute
-
- */
- REGEX * lastprogram = NIL_REG;
- FLAG lastmethod = NOT_VALID; /* FORWARD, REVERSE */
-
- char prevexpr [maxLINE_LEN]; /* Buffer for previous expr. */
- FLAG prevmethod = NOT_VALID; /* FORWARD, REVERSE */
-
- void do_search ();
-
- void
- re_search ()
- {
- do_search (lastprogram, lastmethod);
- }
-
- void
- prev_search ()
- {
- search_for (prevexpr, prevmethod);
- }
-
- void
- search (message, method)
- char * message;
- FLAG method;
- {
- register REGEX * program;
- char prevexp_buf [maxLINE_LEN]; /* Buffer for previous expr. */
-
- if (lastmethod != NOT_VALID) copy_string (prevexp_buf, exp_buf);
-
- /* Get the expression */
- if ((program = get_expression (message)) == NIL_REG)
- return;
-
- if (program != NIL_REG && lastmethod != NOT_VALID) {
- copy_string (prevexpr, prevexp_buf);
- prevmethod = lastmethod;
- }
- lastprogram = program;
- lastmethod = method;
-
- re_search ();
- }
-
- void
- search_for (expr, method)
- char * expr;
- FLAG method;
- {
- register REGEX * program;
-
- /* make the expression */
- if ((program = make_expression (expr)) == NIL_REG)
- return;
- do_search (program, method);
- }
-
- void
- do_search (program, method)
- register REGEX * program;
- FLAG method;
- {
- register LINE * match_line;
-
- if (method == NOT_VALID) {
- error ("No previous search", NIL_PTR);
- return;
- }
- if (program == NIL_REG) {
- error ("No previous search expression", NIL_PTR);
- return;
- }
-
- set_cursor (0, YMAX);
- clear_status ();
- flush ();
- /* Find the match */
- if ((match_line = match (program, cur_text, method)) == NIL_LINE) {
- if (quit == TRUE) {
- status_msg ("Aborted");
- /* swallow_dummy_quit_char (); */
- quit = FALSE;
- }
- else
- status_msg ("Pattern not found");
- return;
- }
-
- /* clear_status (); */
- move_address (program->start_ptr, find_y (match_line));
- }
-
- /* Now the search and replace utilities */
- /* ------------------------------------ */
- /* Opcodes for characters */
- #define NORMAL 0x0200
- #define DOT 0x0400
- #define EOLN 0x0800
- #define STAR 0x1000
- #define BRACKET 0x2000
- #define NEGATE 0x0100
- #define DONE 0x4000
-
- /* Mask for opcodes and characters */
- #define LOW_BYTE 0x00FF
- #define HIGH_BYTE 0xFF00
-
- /* Previous is the contents of the previous address (ptr) points to */
- #define previous(ptr) (* ((ptr) - 1))
-
- /* Buffer to store outcome of compilation */
- int exp_buffer [BLOCK_SIZE];
-
- /* Errors often used */
- char * too_long = "Regular expression too long";
-
- /*
- * Reg_error () is called by compile () if something went wrong. It sets the
- * status of the structure to error, and assigns the error field of the union.
- */
- #define reg_error(str) program->status = REG_ERROR, \
- program->result.err_mess = (str)
-
- /*
- * Bcopy copies `bytes' bytes from the `from' address into the `to' address.
- */
- #ifdef vms
- #define defbcopy
- #endif
- #ifdef msdos
- #define defbcopy
- #endif
-
- #ifdef defbcopy /* otherwise also in standard library */
- void
- bcopy (from, to, bytes)
- register char * from, * to;
- register int bytes;
- {
- while (bytes --)
- * to ++ = * from ++;
- }
- #else
- #ifdef sysV
- #define bcopy(from, to, len) memcpy (to, from, len)
- #else
- extern void bcopy ();
- #endif
- #endif
-
- /*
- * Finished () is called when everything went right during compilation. It
- * allocates space for the expression, and copies the expression buffer into
- * this field.
- */
- FLAG
- finished (program, last_exp)
- register REGEX * program;
- int * last_exp;
- {
- register int length = (int) (last_exp - exp_buffer) * sizeof (int);
- /* Allocate space */
- program->result.expression = (int *) alloc (length);
- if (program->result.expression == NIL_INT) {
- ring_bell ();
- error ("Cannot allocate memory for search expression", NIL_PTR);
- return ERRORS;
- }
- else { /* Copy expression. (expression consists of ints!) */
- bcopy (exp_buffer, program->result.expression, length);
- return FINE;
- }
- }
-
- /*
- * Compile compiles the pattern into a more comprehensible form and returns a
- * REGEX structure. If something went wrong, the status field of the structure
- * is set to REG_ERROR and an error message is set into the err_mess field of
- * the union. If all went well the expression is saved and the expression
- * pointer is set to the saved (and compiled) expression.
- */
- FLAG
- compile (pattern, program)
- register uchar * pattern; /* Pointer to pattern */
- REGEX * program;
- {
- register int * expression = exp_buffer;
- int * prev_char; /* Pointer to previous compiled atom */
- int * acct_field = NIL_INT; /* Pointer to last BRACKET start */
- FLAG negate; /* Negate flag for BRACKET */
- uchar low_char; /* Index for chars in BRACKET */
- uchar c;
-
- /* Check for begin of line */
- if (* pattern == '^') {
- program->status = BEGIN_LINE;
- pattern ++;
- }
- else {
- program->status = 0;
- /* If the first character is a '*' we have to assign it here. */
- if (* pattern == '*') {
- * expression ++ = '*' + NORMAL;
- pattern ++;
- }
- }
-
- for (; ;) { c = * pattern ++;
- switch (c) {
- case '.' :
- * expression ++ = DOT;
- break;
- case '$' :
- /*
- * Only means EOLN if it is the last char of the pattern
- */
- if (* pattern == '\0') {
- * expression ++ = EOLN | DONE;
- program->status |= END_LINE;
- return finished (program, expression);
- }
- else
- * expression ++ = NORMAL + '$';
- break;
- case '\0' :
- * expression ++ = DONE;
- return finished (program, expression);
- case '\\' :
- /* If last char, it must! mean a normal '\' */
- if (* pattern == '\0')
- * expression ++ = NORMAL + '\\';
- else
- * expression ++ = NORMAL + * pattern ++;
- break;
- case '*' :
- /*
- * If the previous expression was a [] find out the
- * begin of the list, and adjust the opcode.
- */
- prev_char = expression - 1;
- if (* prev_char & BRACKET)
- * (expression - (* acct_field & LOW_BYTE)) |= STAR;
- else
- * prev_char |= STAR;
- break;
- case '[' :
- /*
- * First field in expression gives information about
- * the list.
- * The opcode consists of BRACKET and if necessary
- * NEGATE to indicate that the list should be negated
- * and/or STAR to indicate a number of sequence of this
- * list.
- * The lower byte contains the length of the list.
- */
- acct_field = expression ++;
- if (* pattern == '^') { /* List must be negated */
- pattern ++;
- negate = TRUE;
- }
- else
- negate = FALSE;
- while (* pattern != ']') {
- if (* pattern == '\0') {
- reg_error ("Missing ]");
- return FINE;
- }
- if (* pattern == '\\')
- pattern ++;
- * expression ++ = * pattern ++;
- if (* pattern == '-') {
- /* Make list of chars */
- low_char = previous (pattern);
- pattern ++; /* Skip '-' */
- if (low_char ++ > * pattern) {
- reg_error ("Bad range in [a-z]");
- return FINE;
- }
- /* Build list */
- while (low_char <= * pattern
- && low_char != '\0')
- /* avoid wrap-around beyond '\377' (loop!) */
- * expression ++ = low_char ++;
- pattern ++;
- }
- if (expression >= & exp_buffer [BLOCK_SIZE]) {
- reg_error (too_long);
- return FINE;
- }
- }
- pattern ++; /* Skip ']' */
- /* Assign length of list in acct field */
- if ((* acct_field = (int) (expression - acct_field)) == 1) {
- reg_error ("Empty []");
- return FINE;
- }
- /* Assign negate and bracket field */
- * acct_field |= BRACKET;
- if (negate == TRUE)
- * acct_field |= NEGATE;
- /*
- * Add BRACKET to opcode of last char in field because
- * a '*' may be following the list.
- */
- previous (expression) |= BRACKET;
- break;
- default :
- * expression ++ = c + NORMAL;
- }
- if (expression == & exp_buffer [BLOCK_SIZE]) {
- reg_error (too_long);
- return FINE;
- }
- }
- /* NOTREACHED */
- }
-
- /*
- * Match gets as argument the program, pointer to place in current line to
- * start from and the method to search for (either FORWARD or REVERSE).
- * Match () will look through the whole file until a match is found.
- * NIL_LINE is returned if no match could be found.
- */
- LINE *
- match (program, string, method)
- REGEX * program;
- uchar * string;
- register FLAG method;
- {
- register LINE * line = cur_line;
- uchar old_char; /* For saving chars */
-
- /* Corrupted program */
- if (program->status == REG_ERROR)
- return NIL_LINE;
-
- /* Check part of text first */
- if (! (program->status & BEGIN_LINE)) {
- if (method == FORWARD) {
- if (line_check (program, string + 1, method) == MATCH)
- return cur_line; /* Match found */
- }
- else if (! (program->status & END_LINE)) {
- old_char = * string; /* Save char and */
- * string = '\n'; /* Assign '\n' for line_check */
- if (line_check (program, line->text, method) == MATCH) {
- * string = old_char; /* Restore char */
- return cur_line; /* Found match */
- }
- * string = old_char; /* No match, but restore char */
- }
- }
-
- /* No match in last (or first) part of line. Check out rest of file */
- do {
- line = (method == FORWARD) ? line->next : line->prev;
- if (line->text == NIL_PTR) { /* Header/tail */
- status_msg ("Search wrapped around end of file");
- continue;
- }
- if (line_check (program, line->text, method) == MATCH)
- return line;
- } while (line != cur_line && quit == FALSE);
-
- /* No match found. */
- return NIL_LINE;
- }
-
- /*
- * Line_check () checks the line (or rather string) for a match. Method
- * indicates FORWARD or REVERSE search. It scans through the whole string
- * until a match is found, or the end of the string is reached.
- */
- int
- line_check (program, string, method)
- register REGEX * program;
- char * string;
- FLAG method;
- {
- register char * textp = string;
-
- /* Assign start_ptr field. We might find a match right away! */
- program->start_ptr = textp;
-
- /* If the match must be anchored, just check the string. */
- if (program->status & BEGIN_LINE)
- return check_string (program, string, NIL_INT);
-
- if (method == REVERSE) {
- /* First move to the end of the string */
- for (textp = string; * textp != '\n'; textp ++)
- ;
- /* Start checking string until the begin of the string is met */
- while (textp >= string) {
- program->start_ptr = textp;
- if (check_string (program, textp --, NIL_INT))
- return MATCH;
- }
- }
- else {
- /* Move through the string until the end of it is found */
- while (quit == FALSE && * textp != '\0') {
- program->start_ptr = textp;
- if (check_string (program, textp, NIL_INT))
- return MATCH;
- if (* textp == '\n')
- break;
- textp ++;
- }
- }
-
- return NO_MATCH;
- }
-
- /*
- * Check_string () checks if a match can be found in the given string.
- * Whenever a STAR is found during matching, then the begin position of
- * the string is marked and the maximum number of matches is performed.
- * Then the function star () is called which starts to finish the match
- * from this position of the string (and expression).
- * Check () returns MATCH for a match, NO_MATCH if the string
- * couldn't be matched or REG_ERROR for an illegal opcode in expression.
- */
- int
- check_string (program, string, expression)
- REGEX * program;
- register uchar * string;
- int * expression;
- {
- register int opcode; /* Holds opcode of next expr. atom */
- uchar c; /* Char that must be matched */
- uchar * mark; /* For marking position */
- int star_fl; /* A star has been born */
-
- if (expression == NIL_INT)
- expression = program->result.expression;
-
- /* Loop until end of string or end of expression */
- while (quit == FALSE && ! (* expression & DONE) &&
- * string != '\0' && * string != '\n') {
- c = * expression & LOW_BYTE; /* Extract match char */
- opcode = * expression & HIGH_BYTE; /* Extract opcode */
- if ((star_fl = (opcode & STAR)) != 0) { /* Check star occurrence */
- opcode &= ~STAR; /* Strip opcode */
- mark = string; /* Mark current position */
- }
- expression ++; /* Increment expr. */
- switch (opcode) {
- case NORMAL :
- if (star_fl)
- while (* string ++ == c) /* Skip all matches */
- ;
- else if (* string ++ != c)
- return NO_MATCH;
- break;
- case DOT :
- string ++;
- if (star_fl) /* Skip to eoln */
- while (* string != '\0' && * string ++ != '\n')
- ;
- break;
- case NEGATE | BRACKET :
- case BRACKET :
- if (star_fl)
- while (in_list (expression, * string ++, c, opcode) == MATCH)
- ;
- else
- if (in_list (expression, * string ++, c, opcode) == NO_MATCH)
- return NO_MATCH;
- expression += c - 1; /* Add length of list */
- break;
- default :
- /* panic ("Corrupted search program", NIL_PTR); */
- ring_bell ();
- error ("Corrupted search program", NIL_PTR);
- sleep (2);
- return NO_MATCH;
- }
- if (star_fl)
- return star (program, mark, string, expression);
- }
- if (* expression & DONE) {
- program->end_ptr = (char *) string; /* Match ends here */
- /*
- * We might have found a match. The last thing to do is check
- * whether a '$' was given at the end of the expression, or
- * the match was found on a null string. (E.g. [a-z]* always
- * matches) unless a ^ or $ was included in the pattern.
- */
- if ((* expression & EOLN) && * string != '\n' && * string != '\0')
- return NO_MATCH;
- if (string == (uchar *) program->start_ptr
- && ! (program->status & BEGIN_LINE)
- && ! (* expression & EOLN))
- return NO_MATCH;
- return MATCH;
- }
- return NO_MATCH;
- }
-
- /*
- * Star () calls check_string () to find out the longest match possible.
- * It searches backwards until the (in check_string ()) marked position
- * is reached, or a match is found.
- */
- int
- star (program, end_position, string, expression)
- REGEX * program;
- register char * end_position;
- register char * string;
- int * expression;
- {
- do {
- string --;
- if (check_string (program, string, expression))
- return MATCH;
- } while (string != end_position);
-
- return NO_MATCH;
- }
-
- /*
- * In_list () checks if the given character is in the list of []. If it is
- * it returns MATCH. If it isn't it returns NO_MATCH. These returns values
- * are reversed when the NEGATE field in the opcode is present.
- */
- int
- in_list (list, c, list_length, opcode)
- register int * list;
- uchar c;
- register int list_length;
- int opcode;
- {
- if (c == '\0' || c == '\n') /* End of string, never matches */
- return NO_MATCH;
- while (list_length -- > 1) { /* > 1, don't check acct_field */
- if ((* list & LOW_BYTE) == c)
- return (opcode & NEGATE) ? NO_MATCH : MATCH;
- list ++;
- }
- return (opcode & NEGATE) ? MATCH : NO_MATCH;
- }
-
- /* ================================================================== *
- * End *
- * ================================================================== */
-